using System;
using System.Collections.Generic;
using Firefly.Framework;
using Firefly.Framework.Components;
using Firefly.Framework.Components.Cameras;
using Firefly.Framework.Components.Terrain;
using Firefly.Framework.Graphics;
using Firefly.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace TerrainExample
{
    public class TerrainTest : Engine
    {
        private QuadTerrain _terrain;
        private ICamera _camera;
        private FillMode _fillMode;
        private TextCompositor _cameraPosition;
        private FrameRateCounter _frameRate;
        private TextCompositor _cameraKey;
        private TextCompositor _engineKey;
        private TextCompositor _terrainKey;
        private TextCompositor _lod;
        private TextCompositor _renderedIndices;
        private float _theta;
        private float _elapsedTimingValue;

        public TerrainTest()
        {
            ScreenWidth = 1024;
            ScreenHeight = 800;
        }

        protected override void Initialize()
        {
            InputService.Instance.MouseManager.ResetAfterUpdate = true;
            base.Initialize();
        }

        protected override void LoadContent()
        {
            base.LoadContent();
            InitializeCamera();
            InitializeTerrain();
            _fillMode = FillMode.Solid; 
            _cameraPosition = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 20f), Color = Color.GreenYellow };
            _cameraPosition.LoadContent();
            _lod = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 35f), Color = Color.GreenYellow };
            _lod.LoadContent();
            _renderedIndices = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 50f), Color = Color.GreenYellow };
            _renderedIndices.LoadContent();
            _frameRate = new FrameRateCounter(this, "Fonts\\Kootenay") { Position = new Vector2(5f, 5f), Color = Color.GreenYellow };
            _frameRate.LoadContent();
            _cameraKey = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 785f), Color = Color.GreenYellow };
            _cameraKey.LoadContent();
            _cameraKey.Text = "[Camera] Forward : W | Left : A | Right : D | Backward : S";
            _engineKey = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 755f), Color = Color.GreenYellow };
            _engineKey.LoadContent();
            _engineKey.Text = "[Engine] Exit : Esc | Toggle FillMode : Tab";
            _terrainKey = new TextCompositor("Fonts\\Kootenay") { Position = new Vector2(5f, 770f), Color = Color.GreenYellow };
            _terrainKey.LoadContent();
            _terrainKey.Text = "[Terrain] Increase LOD : + | Decrease LOD : -";
        }

        protected override void UnloadContent()
        {
            _terrain.Dispose();
            _cameraPosition.Dispose();
            _frameRate.Dispose();
            _cameraKey.Dispose();
            _engineKey.Dispose();
        }

        protected override void Update(GameTime gameTime)
        {
            _elapsedTimingValue = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 10f;
            _theta += 0.002f * _elapsedTimingValue;
            float xV3 = (float)Math.Sin(_theta);
            float zV3 = (float)Math.Cos(_theta);
            Vector3 sunlightDirection = new Vector3(xV3, .6f, zV3);
            Vector3.Normalize(sunlightDirection);
            base.Update(gameTime);
            _camera.Update(gameTime);
            _terrain.Shader.SunlightDirection = sunlightDirection;
            _terrain.Update(gameTime);
            _cameraPosition.Text = string.Format("Camera Position: {0}", _camera.Position);
            _frameRate.Update(gameTime);
            _lod.Text = string.Format("Terrain LOD: {0}", _terrain.LevelOfDetail);
            _renderedIndices.Text = string.Format("Rendered Indices: {0}", _terrain.RenderedIndices);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsService.Instance.Clear(Color.CornflowerBlue);
            GraphicsService.Instance.FillMode = _fillMode;
            _terrain.Draw(gameTime);
            base.Draw(gameTime);
            GraphicsService.Instance.FillMode = FillMode.Solid;
            _cameraPosition.Draw(gameTime);
            _frameRate.Draw(gameTime);
            _cameraKey.Draw(gameTime);
            _engineKey.Draw(gameTime);
            _terrainKey.Draw(gameTime);
            _lod.Draw(gameTime);
            _renderedIndices.Draw(gameTime);
        }

        private void InitializeCamera()
        {
            _camera = new FreeCamera(this)
            {
                Position = new Vector3(1480, 700, 1260),
                Pitch = (float)-(Math.PI / 2)
            };
        }

        private void InitializeTerrain()
        {
            _terrain = new QuadTerrain(this, "\\Textures\\Terrain\\Astoria", 17, 257, 20, 5f, _camera) { DrawOrder = 0, LevelOfDetail = 3.5f };
            _terrain.Shader.BlendTextureAsset = "\\Textures\\Terrain\\AstoriaBlend";
            _terrain.Shader.BlackTextureAsset = "\\Textures\\Terrain\\WaterTexture";
            _terrain.LoadContent();
            _terrain.Shader.SunlightDirection = Vector3.Normalize(new Vector3(.5f, .5f, .8f));
            _terrain.Shader.SunlightColor = new Vector3(2.3f, 2f, 1.8f);
        }
        
        #region IInputProcessor Members

        public override void InitializeInputMaps()
        {
            HoldThreshold = 100;
            KeyMaps = new List<KeyMap>();
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.Escape }));
            KeyMaps[0].MapPressed += new EventHandler<KeyMapEventArgs>(OnExit);
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.Tab }));
            KeyMaps[1].MapPressed += new EventHandler<KeyMapEventArgs>(OnToggleFillMode);
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.OemMinus }));
            KeyMaps[2].MapPressed += new EventHandler<KeyMapEventArgs>(OnDecreaseLod);
            KeyMaps[2].MapHeld += new EventHandler<KeyMapEventArgs>(OnDecreaseLod);
            KeyMaps[2].HoldThreshold = HoldThreshold;
            KeyMaps.Add(new KeyMap(new Keys[] { Keys.OemPlus }));
            KeyMaps[3].MapPressed += new EventHandler<KeyMapEventArgs>(OnIncreaseLod);
            KeyMaps[3].MapHeld += new EventHandler<KeyMapEventArgs>(OnIncreaseLod);
            KeyMaps[3].HoldThreshold = HoldThreshold;
        }

        private void OnDecreaseLod(object sender, KeyMapEventArgs e)
        {
            _terrain.LevelOfDetail -= 0.1f;
        }

        private void OnIncreaseLod(object sender, KeyMapEventArgs e)
        {
            _terrain.LevelOfDetail += 0.1f;
        }

        private void OnToggleFillMode(object sender, KeyMapEventArgs e)
        {
            if (_fillMode == FillMode.WireFrame)
            {
                _fillMode = FillMode.Solid;
            }
            else
            {
                if (_fillMode == FillMode.Solid)
                {
                    _fillMode = FillMode.Point;
                }
                else
                {
                    if (_fillMode == FillMode.Point)
                    {
                        _fillMode = FillMode.WireFrame;
                    }
                }
            }
        }

        private void OnExit(object sender, KeyMapEventArgs e)
        {
            Exit();
        }

        #endregion
    }
}
